#include "StompedReality/Asset/TextureSystem.h"
#include "StompedReality/Core/FileSystem.h"
#include "StompedReality/Core/IGame.h"
#include "StompedReality/Core/Logger.h"
#include <GL/gl.h>
#include <stb_image/stb_image.h>
StompedReality::Asset::TextureSystem::TextureSystem( ) :
  StompedReality::GameAccess( )
{
  this->m_TextureList = 0;
  this->m_Capacity = 0;
}

StompedReality::Asset::TextureSystem::~TextureSystem( )
{
  LogCallFunctionInfo( "TextureSystem", "Destructor" );

  for ( int i = 0; i < this->m_Capacity; ++i )
  {
    if ( this->HasTextureInfo ( i ) )
    {
      if ( this->HasTextureInfo ( i ) )
      {
        StompedReality::ID id = this->GetGLTextureBindingID ( i );
        glDeleteTextures ( 1, &id );
      }
    }
  }
  delete[] this->m_TextureList;
}

void StompedReality::Asset::TextureSystem::Initialize ( int capacity )
{
  if ( this->m_Capacity != 0 )
  {
    LogCallFunctionWarning( "TextureSystem", "Initialize", "re-initialization" );
    return;
  }
  this->m_TextureList = new StompedReality::Asset::TextureInfo[capacity];
  this->m_Capacity = capacity;
  LogCallFunctionInfo( "TextureSystem", "Initialize" );
  LogInfoPair( "capacity", capacity );
}

bool StompedReality::Asset::TextureSystem::OpenTexture ( StompedReality::Asset::TextureInfo& info,
                                                         const char* fileName )
{
  int channels = 0;
  unsigned char* imgdata = stbi_load ( fileName,
                                       &( info.width ), &( info.height ), &channels,
                                       0 );

  // check cahnnels
  if ( 4 != channels )
  {
    info.width = 0;
    info.height = 0;
    stbi_image_free ( imgdata );
    LogCallFunctionWarning( "TextureSystem", "OpenTexture", "failed to load" );
    LogInfoPair( "file", fileName );

    return false;
  }

  // load texture to gpu memory
  glGenTextures ( 1, &( info.glBinding ) );
  glBindTexture ( GL_TEXTURE_2D, info.glBinding );
  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
  glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGBA8,
                 info.width, info.height,
                 0, GL_RGBA, GL_UNSIGNED_BYTE,
                 imgdata );
  stbi_image_free  ( imgdata );
  return true;
}

bool StompedReality::Asset::TextureSystem::OpenTexture ( ID id, const char* fileName, bool global )
{
  if ( this->HasTextureInfo ( id ) )
  {
    LogCallFunctionWarning( "TextureSystem", "openTexture", "duplicate id" );
    LogInfoPair( "file", fileName );
    LogInfoPair( "id", id );
    return false;
  }
  int channels = 0;
  TextureInfo info;
  if ( !this->OpenTexture ( info, fileName ) )
  {
    return false;
  }
  this->m_TextureList[id] = info;

  LogCallFunctionInfo( "TextureSystem", "openTexture" );
  LogInfoPair( "file", fileName );
  LogInfoPair( "id", id );

  return true;
}

bool StompedReality::Asset::TextureSystem::HasTextureInfo ( const ID id )
{
  return ( this->m_TextureList[id].width > 0 && this->m_TextureList[id].height > 0 );
}

const StompedReality::Asset::TextureInfo StompedReality::Asset::TextureSystem::GetTextureInfo ( const ID id )
{
  return this->m_TextureList[id];
}

const StompedReality::ID StompedReality::Asset::TextureSystem::GetGLTextureBindingID ( const ID id )
{
  return this->m_TextureList[id].glBinding;
}

bool StompedReality::Asset::TextureSystem::ReadFromFile ( const char* fileName )
{
  auto root = *(this->m_Game->getFileSystem( )->openAsJson( fileName ));
  for ( unsigned i = 0; i < root["Textures"].size( ); ++i )
  {
    this->OpenTexture ( root["Textures"][i]["id"].asUInt( ),
                        root["Textures"][i]["file"].asCString( ),
                        root["Textures"][i].get ( "global", false ).asBool( ) );
  }
  return true;
}

bool StompedReality::Asset::TextureSystem::ParseConfig ( Json::Value& root )
{
  LogCallFunctionInfo( "PatchSystem", "ParseConfig" )

  this->Initialize ( root["TextureSystem"].get ( "capacity", 512 ).asInt( ) );
  return true;
}

void StompedReality::Asset::TextureSystem::FreeTexture ( StompedReality::ID id )
{
  StompedReality::ID glID = this->GetGLTextureBindingID ( id );
  glDeleteTextures ( 1, &glID );
}

